/**
 * Copyright (c) 2003, 2004, Chess iT
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of Chess iT, nor the names of its contributors may be used 
 *   to endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package nl.chess.it.util.config.examples.extended;

import nl.chess.it.util.config.ConfigValidationResult;
import nl.chess.it.util.config.examples.simple.SimpleConfig;

import org.apache.log4j.Logger;

import java.util.Iterator;


/**
 * This class makes sure the correct order is used in initializing the various part of the application. It will also complain if
 * the surroundings of the application are not correctly configured (i.e., no valid configuration file given).
 * 
 * <p>
 * It serves as the main Singleton class of this application.
 * </p>
 *
 * @author Guus Bosman (Chess iT)
 * @version $Revision: 1.1.1.1 $
 */
public class Application {
    private static Application instance = new Application();
    private Logger logger = Logger.getLogger(Application.class);
    private SimpleConfig config;

    private Application() {
        logger.info("Starting application.");

        displayVersionInformation();

        readConfiguration();

        logger.info("Succesfully started application.");
    }

    /**
     * Displays information about who built this application on the logger. If anything goes wrong a warning is displayed but the
     * application continues.
     */
    private void displayVersionInformation() {
        VersionInformation versionInformation = null;

        try {
            versionInformation = new VersionInformation();
        }
        catch (Exception e) {
            logger.warn("Couldn't read version information (not fatal).", e);

            return;
        }

        ConfigValidationResult result = versionInformation.validateConfiguration();

        if (result.thereAreErrors()) {
            logger.warn("There are errors in the version information file (not fatal).");

            for (Iterator iter = result.getErrors().iterator(); iter.hasNext();) {
                logger.debug(" > " + iter.next());
            }

            return;
        }

        if (result.thereAreUnusedProperties()) {
            logger.debug("Unused properties in version information: " + result.getUnusedProperties());
        }

        // this is what's this method's all about: a display in the logger
        logger.info("Application version " + versionInformation.getVersion() + ", built by " + versionInformation.getBuiltBy() +
                    " on " + versionInformation.getBuiltOn() + ".");
    }

    /**
     * Report a (fatal) starting up error and shut down the application.
     *
     * @param string Human readable description of the problem.
     * @param e (Optional) exception that explains the situation a bit better.
     */
    private void fatalError(String string, Throwable e) {
        String niceDescription = "Ending application because " + string + ".";

        if (e == null) {
            logger.fatal(niceDescription);
        }
        else {
            logger.fatal(niceDescription, e);
        }

        System.err.println(niceDescription);
        endApplication();
    }

    /**
     * Reads and validates the configuration file.
     */
    private void readConfiguration() {
        logger.debug("Reading configuration from " + SimpleConfig.RESOURCE_NAME);

        try {
            config = new SimpleConfig(SimpleConfig.RESOURCE_NAME);
        }
        catch (Exception e) {
            fatalError("Couldn't read configuration.", e);
        }

        ConfigValidationResult result = config.validateConfiguration();

        if (result.thereAreErrors()) {
            logger.fatal("There are errors in the configuration.");

            for (Iterator iter = result.getErrors().iterator(); iter.hasNext();) {
                logger.error(" > " + iter.next());
            }

            fatalError("there are errors in the configuration", null);
        }

        if (result.thereAreUnusedProperties()) {
            logger.info("Unused properties: " + result.getUnusedProperties());
        }
    }

    public static Application getInstance() {
        return instance;
    }

    /**
     * Ends the application.
     */
    private void endApplication() {
        logger.debug("Ending application.");
        System.exit(1);
    }

    public SimpleConfig getConfig() {
        return config;
    }
}
